// 事件分发器接口

using System;
using DanMuHelper.Pool;
using System.Collections.Generic;

namespace DanMuHelper.Event
{
    internal class SignalReceiver<T>
        where T : System.Delegate
    {
        public T handler;
        public int priority;
        public object context;
        public bool oneshot;
        public bool purged;
    }

    public enum InsertReceiverResult
    {
        Success,
        Pending,
        Duplicated,
    }

    internal class SignalBackend<T>
        where T : System.Delegate
    {
        public List<SignalReceiver<T>> receiviers;
        public int emitting;
        List<SignalReceiver<T>> pendings;

        private static int Locate(List<SignalReceiver<T>> container, T handler, object context)
        {
            if (null != container)
            {
                var nullContext = System.Object.ReferenceEquals(context, null);
                for (int i = container.Count - 1; i >= 0; i--)
                {
                    if (
                        container[i].handler == handler
                        && (nullContext || System.Object.ReferenceEquals(container[i].context, context))
                    )
                    {
                        return i;
                    }
                }
            }
            return -1;
        }

        private static SignalReceiver<T> Make(T handler, object context, int priority, bool oneshot)
        {
            var ret = new SignalReceiver<T>();
            ret.handler = handler;
            ret.context = context;
            ret.priority = priority;
            ret.oneshot = oneshot;
            return ret;
        }

        public bool Insert(T handler, object context, int priority, bool oneshot)
        {
            if (emitting > 0)
            {
                return AddPending(handler, context, priority, oneshot);
            }
            var nullContext = System.Object.ReferenceEquals(context, null);
            var insertPosition = 0;
            var currentPosition = -1;
            if (null == receiviers)
            {
                receiviers = ListPool<SignalReceiver<T>>.Create();
            }
            else
            {
                var n = receiviers.Count;
                for (int i = 0; i < n; i++)
                {
                    var tmp = receiviers[i];
                    if (priority > tmp.priority)
                    {
                        insertPosition++;
                    }
                    if (
                        handler == tmp.handler
                        && !tmp.purged
                        && (nullContext || System.Object.ReferenceEquals(context, tmp.context))
                    )
                    {
                        currentPosition = i;
                        break;
                    }
                }
                if (currentPosition >= 0)
                {
                    return false;
                }
            }
            receiviers.Insert(insertPosition, Make(handler, context, priority, oneshot));
            return true;
        }

        public void Remove(T handler)
        {
            RemovePending(handler);
            if (null != receiviers)
            {
                if (emitting > 0)
                {
                    receiviers.ForEach(
                        (item) =>
                        {
                            if (!item.purged && item.handler == handler)
                            {
                                item.purged = true;
                            }
                        }
                    );
                }
                else
                {
                    var removed = receiviers.RemoveAll(
                        (item) =>
                        {
                            return !item.purged && item.handler == handler;
                        }
                    );
                    if (removed > 0 && receiviers.Count == 0)
                    {
                        ListPool<SignalReceiver<T>>.Release(ref receiviers);
                    }
                }
            }
        }

        public void RemoveByContext(object context)
        {
            if (System.Object.ReferenceEquals(context, null))
            {
                return;
            }
            if (null != pendings)
            {
                var removed = pendings.RemoveAll(
                    (item) =>
                    {
                        return System.Object.ReferenceEquals(item.context, context);
                    }
                );
                if (removed > 0 && pendings.Count == 0)
                {
                    ListPool<SignalReceiver<T>>.Release(ref pendings);
                }
            }
            if (null != receiviers)
            {
                if (emitting > 0)
                {
                    receiviers.ForEach(
                        (item) =>
                        {
                            if (System.Object.ReferenceEquals(item.context, context) && !item.purged)
                            {
                                item.purged = true;
                            }
                        }
                    );
                }
                else
                {
                    var removed = receiviers.RemoveAll(
                        (item) =>
                        {
                            return System.Object.ReferenceEquals(item.context, context);
                        }
                    );
                    if (removed > 0 && receiviers.Count == 0)
                    {
                        ListPool<SignalReceiver<T>>.Release(ref receiviers);
                    }
                }
            }
        }

        public void Clear()
        {
            if (null != pendings)
            {
                ListPool<SignalReceiver<T>>.Release(ref pendings);
            }
            if (null != receiviers)
            {
                if (emitting > 0)
                {
                    receiviers.ForEach(
                        (item) =>
                        {
                            if (!item.purged)
                            {
                                item.purged = true;
                            }
                        }
                    );
                }
                else
                {
                    ListPool<SignalReceiver<T>>.Release(ref receiviers);
                }
            }
        }

        public bool AddPending(T handler, object context, int priority, bool oneshot)
        {
            if (null == pendings)
            {
                pendings = ListPool<SignalReceiver<T>>.Create();
                pendings.Add(Make(handler, context, priority, oneshot));
                return true;
            }
            if (Locate(pendings, handler, context) >= 0)
            {
                return false;
            }
            pendings.Add(Make(handler, context, priority, oneshot));
            return true;
        }

        public bool RemovePending(T handler)
        {
            if (null != pendings)
            {
                var pos = Locate(pendings, handler, null);
                if (pos >= 0)
                {
                    pendings.RemoveAt(pos);
                    if (pendings.Count == 0)
                    {
                        ListPool<SignalReceiver<T>>.Release(ref pendings);
                    }
                    return true;
                }
            }
            return false;
        }

        public void ProcessPendings()
        {
            if (null == pendings)
            {
                return;
            }
            for (int i = pendings.Count - 1; i >= 0; i--)
            {
                var tmp = pendings[i];
                Insert(tmp.handler, tmp.context, tmp.priority, tmp.oneshot);
            }
            ListPool<SignalReceiver<T>>.Release(ref pendings);
        }

        public void Purge()
        {
            if (null == receiviers)
            {
                return;
            }
            var count = receiviers.Count;
            for (int i = count - 1; i >= 0; i--)
            {
                if (receiviers[i].purged)
                {
                    receiviers.RemoveAt(i);
                }
            }
            if (count != receiviers.Count && receiviers.Count == 0)
            {
                ListPool<SignalReceiver<T>>.Release(ref receiviers);
            }
        }
    }

    public class Signal
    {
        /// <summary>
        /// 可以中断
        /// </summary>
        protected bool _interruptable;

        /// <summary>
        /// 安全模式，事件回调报错后，仍然继续发送消息
        /// </summary>
        protected bool _safeMode = true;

        internal SignalBackend<SignalHandler> _backend;

        public virtual bool interruptable
        {
            get { return _interruptable; }
            set { _interruptable = value; }
        }

        public virtual bool safeMode
        {
            get { return _safeMode; }
            set { _safeMode = value; }
        }

        public void Subscribe(
            SignalHandler handler,
            int priority = SignalSetting.DEFAULT_PRIORITY,
            bool oneshot = false
        )
        {
            //Subscribe(handler, null, priority, oneshot);
            if (null == _backend)
            {
                _backend = new SignalBackend<SignalHandler>();
            }
            _backend.Insert(handler, null, priority, oneshot);
        }

        [Obsolete("接口废弃")]
        public void Subscribe(
            SignalHandler handler,
            object context,
            int priority = SignalSetting.DEFAULT_PRIORITY,
            bool oneshot = false
        )
        {
            if (null == _backend)
            {
                _backend = new SignalBackend<SignalHandler>();
            }
            _backend.Insert(handler, context, priority, oneshot);
        }

        public void Unsubscribe(SignalHandler handler)
        {
            if (null != _backend)
            {
                _backend.Remove(handler);
            }
        }

        [System.Obsolete("context参数即将废弃")]
        public void UnsubscribeByContext(object context)
        {
            if (null != _backend)
            {
                _backend.RemoveByContext(context);
            }
        }

        public void UnsubscribeAll()
        {
            if (null != _backend)
            {
                _backend.Clear();
            }
        }

        public void Emit()
        {
            if (null == _backend)
                return;

            // if (0 == _backend.emitting)
            // {
            //     _backend.ProcessPendings();
            //     _backend.Purge();
            // }

            _backend.emitting++;

            var n = null != _backend.receiviers ? _backend.receiviers.Count : 0;
            for (int i = 0; i < n; i++)
            {
                var r = _backend.receiviers[i];
                if (!r.purged)
                {
                    //#if UNITY_EDITOR
                    //                    try { r.handler.Invoke(); } catch (SignalInterruptor) { if (_interruptable) { break; } }
                    //#else
                    try
                    {
                        r.handler.Invoke();
                    }
                    catch (SignalInterruptor)
                    {
                        if (_interruptable)
                        {
                            break;
                        }
                    }
                    catch (System.Exception e)
                    {
                        UnityEngine.Debug.LogError($"{e} -- Signal emit error {r.GetType().FullName} - {r.handler} / {e.Message}");
                        if (!_safeMode)
                        {
                            throw e;
                        }
#if UNITY_EDITOR || DEVELOPMENT_BUILD
                        throw e;
#endif
                    }
                    //#endif
                    if (r.oneshot)
                    {
                        r.purged = true;
                    }
                }
            }
            _backend.emitting--;
            if (0 == _backend.emitting)
            {
                _backend.ProcessPendings();
                _backend.Purge();
            }
        }
    }

    public class Signal<T1>
    {
        /// <summary>
        /// 可以中断
        /// </summary>
        protected bool _interruptable;

        /// <summary>
        /// 安全模式，事件回调报错后，仍然继续发送消息
        /// </summary>
        protected bool _safeMode = true;

        internal SignalBackend<SignalHandler<T1>> _backend;

        public virtual bool interruptable
        {
            get { return _interruptable; }
            set { _interruptable = value; }
        }

        public virtual bool safeMode
        {
            get { return _safeMode; }
            set { _safeMode = value; }
        }

        public void Subscribe(
            SignalHandler<T1> handler,
            int priority = SignalSetting.DEFAULT_PRIORITY,
            bool oneshot = false
        )
        {
            //Subscribe(handler, null, priority, oneshot);
            if (null == _backend)
            {
                _backend = new SignalBackend<SignalHandler<T1>>();
            }
            _backend.Insert(handler, null, priority, oneshot);
        }

        [Obsolete("接口即将废弃")]
        public void Subscribe(
            SignalHandler<T1> handler,
            object context,
            int priority = SignalSetting.DEFAULT_PRIORITY,
            bool oneshot = false
        )
        {
            if (null == _backend)
            {
                _backend = new SignalBackend<SignalHandler<T1>>();
            }
            _backend.Insert(handler, context, priority, oneshot);
        }

        public void Unsubscribe(SignalHandler<T1> handler)
        {
            if (null != _backend)
            {
                _backend.Remove(handler);
            }
        }

        [System.Obsolete("context参数即将废弃")]
        public void UnsubscribeByContext(object context)
        {
            if (null != _backend)
            {
                _backend.RemoveByContext(context);
            }
        }

        public void UnsubscribeAll()
        {
            if (null != _backend)
            {
                _backend.Clear();
            }
        }

        public void Emit(T1 arg1)
        {
            if (null == _backend)
                return;

            _backend.emitting++;

            var n = null != _backend.receiviers ? _backend.receiviers.Count : 0;
            for (int i = 0; i < n; i++)
            {
                var r = _backend.receiviers[i];
                if (!r.purged)
                {
                    //#if UNITY_EDITOR
                    //                    try { r.handler.Invoke(arg1); } catch (SignalInterruptor) { if (_interruptable) { break; } }
                    //#else
                    try
                    {
                        r.handler.Invoke(arg1);
                    }
                    catch (SignalInterruptor)
                    {
                        if (_interruptable)
                        {
                            break;
                        }
                    }
                    catch (System.Exception e)
                    {
                        UnityEngine.Debug.LogError($"{e} -- Signal emit error {r.GetType().FullName} - {r.handler} / {e.Message}");
                        if (!_safeMode)
                        {
                            // ExceptionDispatchInfo.Capture(e.InnerException).Throw();
                            throw e;
                        }
#if UNITY_EDITOR || DEVELOPMENT_BUILD
                        throw e;
#endif
                    }
                    //#endif
                    if (r.oneshot)
                    {
                        r.purged = true;
                    }
                }
            }
            _backend.emitting--;
            if (0 == _backend.emitting)
            {
                _backend.ProcessPendings();
                _backend.Purge();
            }
        }
    }

    public class Signal<T1, T2>
    {
        /// <summary>
        /// 可以中断
        /// </summary>
        protected bool _interruptable;

        /// <summary>
        /// 安全模式，事件回调报错后，仍然继续发送消息
        /// </summary>
        protected bool _safeMode = true;

        internal SignalBackend<SignalHandler<T1, T2>> _backend;

        public virtual bool interruptable
        {
            get { return _interruptable; }
            set { _interruptable = value; }
        }

        public virtual bool safeMode
        {
            get { return _safeMode; }
            set { _safeMode = value; }
        }

        public void Subscribe(
            SignalHandler<T1, T2> handler,
            int priority = SignalSetting.DEFAULT_PRIORITY,
            bool oneshot = false
        )
        {
            //Subscribe(handler, null, priority, oneshot);
            if (null == _backend)
            {
                _backend = new SignalBackend<SignalHandler<T1, T2>>();
            }
            _backend.Insert(handler, null, priority, oneshot);
        }

        [Obsolete]
        public void Subscribe(
            SignalHandler<T1, T2> handler,
            object context,
            int priority = SignalSetting.DEFAULT_PRIORITY,
            bool oneshot = false
        )
        {
            if (null == _backend)
            {
                _backend = new SignalBackend<SignalHandler<T1, T2>>();
            }
            _backend.Insert(handler, context, priority, oneshot);
        }

        public void Unsubscribe(SignalHandler<T1, T2> handler)
        {
            if (null != _backend)
            {
                _backend.Remove(handler);
            }
        }

        [System.Obsolete("context参数即将废弃")]
        public void UnsubscribeByContext(object context)
        {
            if (null != _backend)
            {
                _backend.RemoveByContext(context);
            }
        }

        public void UnsubscribeAll()
        {
            if (null != _backend)
            {
                _backend.Clear();
            }
        }

        public void Emit(T1 arg1, T2 arg2)
        {
            if (null == _backend)
                return;

            _backend.emitting++;

            var n = null != _backend.receiviers ? _backend.receiviers.Count : 0;
            for (int i = 0; i < n; i++)
            {
                var r = _backend.receiviers[i];
                if (!r.purged)
                {
                    //#if UNITY_EDITOR
                    //                    try { r.handler.Invoke(arg1, arg2); } catch (SignalInterruptor) { if (_interruptable) { break; } }
                    //#else
                    try
                    {
                        r.handler.Invoke(arg1, arg2);
                    }
                    catch (SignalInterruptor)
                    {
                        if (_interruptable)
                        {
                            break;
                        }
                    }
                    catch (System.Exception e)
                    {
                        UnityEngine.Debug.LogError($"{e} -- Signal emit error {r.GetType().FullName} - {r.handler} / {e.Message}");
                        // TLog.Error("SignalException", e.Message + "\n" + e.StackTrace);
                        if (!_safeMode)
                        {
                            // ExceptionDispatchInfo.Capture(e.InnerException).Throw();
                            throw e;
                        }
#if UNITY_EDITOR || DEVELOPMENT_BUILD
                        throw e;
#endif
                    }
                    //#endif
                    if (r.oneshot)
                    {
                        r.purged = true;
                    }
                }
            }
            _backend.emitting--;
            if (0 == _backend.emitting)
            {
                _backend.ProcessPendings();
                _backend.Purge();
            }
        }
    }

    public class Signal<T1, T2, T3>
    {
        /// <summary>
        /// 可以中断
        /// </summary>
        protected bool _interruptable;

        /// <summary>
        /// 安全模式，事件回调报错后，仍然继续发送消息
        /// </summary>
        protected bool _safeMode = true;

        internal SignalBackend<SignalHandler<T1, T2, T3>> _backend;

        public virtual bool interruptable
        {
            get { return _interruptable; }
            set { _interruptable = value; }
        }

        public virtual bool safeMode
        {
            get { return _safeMode; }
            set { _safeMode = value; }
        }

        public void Subscribe(
            SignalHandler<T1, T2, T3> handler,
            int priority = SignalSetting.DEFAULT_PRIORITY,
            bool oneshot = false
        )
        {
            //Subscribe(handler, null, priority, oneshot);
            if (null == _backend)
            {
                _backend = new SignalBackend<SignalHandler<T1, T2, T3>>();
            }
            _backend.Insert(handler, null, priority, oneshot);
        }

        [Obsolete]
        public void Subscribe(
            SignalHandler<T1, T2, T3> handler,
            object context,
            int priority = SignalSetting.DEFAULT_PRIORITY,
            bool oneshot = false
        )
        {
            if (null == _backend)
            {
                _backend = new SignalBackend<SignalHandler<T1, T2, T3>>();
            }
            _backend.Insert(handler, context, priority, oneshot);
        }

        public void Unsubscribe(SignalHandler<T1, T2, T3> handler)
        {
            if (null != _backend)
            {
                _backend.Remove(handler);
            }
        }

        [System.Obsolete("context参数即将废弃")]
        public void UnsubscribeByContext(object context)
        {
            if (null != _backend)
            {
                _backend.RemoveByContext(context);
            }
        }

        public void UnsubscribeAll()
        {
            if (null != _backend)
            {
                _backend.Clear();
            }
        }

        public void Emit(T1 arg1, T2 arg2, T3 arg3)
        {
            if (null == _backend)
                return;

            _backend.emitting++;

            var n = null != _backend.receiviers ? _backend.receiviers.Count : 0;
            for (int i = 0; i < n; i++)
            {
                var r = _backend.receiviers[i];
                if (!r.purged)
                {
                    //#if UNITY_EDITOR
                    //                    try { r.handler.Invoke(arg1, arg2, arg3); } catch (SignalInterruptor) { if (_interruptable) { break; } }
                    //#else
                    try
                    {
                        r.handler.Invoke(arg1, arg2, arg3);
                    }
                    catch (SignalInterruptor)
                    {
                        if (_interruptable)
                        {
                            break;
                        }
                    }
                    catch (System.Exception e)
                    {
                        UnityEngine.Debug.LogError($"{e} -- Signal emit error {r.GetType().FullName} - {r.handler} / {e.Message}");
                        if (!_safeMode)
                        {
                            // ExceptionDispatchInfo.Capture(e.InnerException).Throw();
                            throw e;
                        }
#if UNITY_EDITOR || DEVELOPMENT_BUILD
                        throw e;
#endif
                    }
                    //#endif
                    if (r.oneshot)
                    {
                        r.purged = true;
                    }
                }
            }
            _backend.emitting--;
            if (0 == _backend.emitting)
            {
                _backend.ProcessPendings();
                _backend.Purge();
            }
        }
    }

    public class Signal<T1, T2, T3, T4>
    {
        /// <summary>
        /// 可以中断
        /// </summary>
        protected bool _interruptable;

        /// <summary>
        /// 安全模式，事件回调报错后，仍然继续发送消息
        /// </summary>
        protected bool _safeMode = true;

        internal SignalBackend<SignalHandler<T1, T2, T3, T4>> _backend;

        public virtual bool interruptable
        {
            get { return _interruptable; }
            set { _interruptable = value; }
        }

        public virtual bool safeMode
        {
            get { return _safeMode; }
            set { _safeMode = value; }
        }

        public void Subscribe(
            SignalHandler<T1, T2, T3, T4> handler,
            int priority = SignalSetting.DEFAULT_PRIORITY,
            bool oneshot = false
        )
        {
            //Subscribe(handler, null, priority, oneshot);
            if (null == _backend)
            {
                _backend = new SignalBackend<SignalHandler<T1, T2, T3, T4>>();
            }
            _backend.Insert(handler, null, priority, oneshot);
        }

        [Obsolete]
        public void Subscribe(
            SignalHandler<T1, T2, T3, T4> handler,
            object context,
            int priority = SignalSetting.DEFAULT_PRIORITY,
            bool oneshot = false
        )
        {
            if (null == _backend)
            {
                _backend = new SignalBackend<SignalHandler<T1, T2, T3, T4>>();
            }
            _backend.Insert(handler, context, priority, oneshot);
        }

        public void Unsubscribe(SignalHandler<T1, T2, T3, T4> handler)
        {
            if (null != _backend)
            {
                _backend.Remove(handler);
            }
        }

        [System.Obsolete("context参数即将废弃")]
        public void UnsubscribeByContext(object context)
        {
            if (null != _backend)
            {
                _backend.RemoveByContext(context);
            }
        }

        public void UnsubscribeAll()
        {
            if (null != _backend)
            {
                _backend.Clear();
            }
        }

        public void Emit(T1 arg1, T2 arg2, T3 arg3, T4 arg4)
        {
            if (null == _backend)
                return;

            _backend.emitting++;

            var n = null != _backend.receiviers ? _backend.receiviers.Count : 0;
            for (int i = 0; i < n; i++)
            {
                var r = _backend.receiviers[i];
                if (!r.purged)
                {
                    //#if UNITY_EDITOR
                    //                    try { r.handler.Invoke(arg1, arg2, arg3); } catch (SignalInterruptor) { if (_interruptable) { break; } }
                    //#else
                    try
                    {
                        r.handler.Invoke(arg1, arg2, arg3, arg4);
                    }
                    catch (SignalInterruptor)
                    {
                        if (_interruptable)
                        {
                            break;
                        }
                    }
                    catch (System.Exception e)
                    {
                        UnityEngine.Debug.LogError($"{e} -- Signal emit error {r.GetType().FullName} - {r.handler} / {e.Message}");
                        if (!_safeMode)
                        {
                            // ExceptionDispatchInfo.Capture(e.InnerException).Throw();
                            throw e;
                        }
#if UNITY_EDITOR || DEVELOPMENT_BUILD
                        throw e;
#endif
                    }
                    //#endif
                    if (r.oneshot)
                    {
                        r.purged = true;
                    }
                }
            }
            _backend.emitting--;
            if (0 == _backend.emitting)
            {
                _backend.ProcessPendings();
                _backend.Purge();
            }
        }
    }
}
